package com.liang.recommend.algorithm;

import com.liang.recommend.pojo.P;
import com.liang.recommend.pojo.UserLike;

import java.util.List;

//潜在因子算法
public class Latent_Factor {

    public static void main(String[] args) {
        forTest();
    }

    public static void forTest(){
        /*
         模拟3个用户 5个潜在因子
         */
        Double[][] q ={
                {1.0,2.0,3.0,4.0,5.0},
                {1.0,2.0,3.0,4.0,5.0},
                {1.0,2.0,3.0,4.0,5.0}
        };
        /*
        模拟4部电影 5个潜在因子
         */
        Double[][] p ={
                {1.0,2.0,3.0,4.0},
                {1.0,2.0,3.0,4.0},
                {1.0,2.0,3.0,4.0},
                {1.0,2.0,3.0,4.0},
                {1.0,2.0,3.0,4.0},
        };
        Latent_Factor factor = new Latent_Factor();
        Double[][] m = factor.getR(q, p);
        for (Double[] n : m) {
            for (Double  u: n) {
                System.out.print(u+"\t");
            }
            System.out.println();
            /*
              得到3个用户相对4部电影的评分矩阵
              15 30 45 60
              15 30 45 60
              15 30 45 60
             */

        }
    }

    /**
     * R=QP 两个矩阵相乘就可以得到估计的得分矩阵
     * *========================================*
     * *        电影1   电影.. 电影size         *
     * * 用户1                                  *
     * * 用户..                                 *
     * * size                                   *
     * *========================================*
     * @param matrixQ Q用户矩阵
     * @param matrixP P电影矩阵
     * @return
     */
    public Double[][] getR(Double[][] matrixQ, Double[][] matrixP){

        int lengthQ = matrixQ.length; //得到多少个用户，决定list的行
        int lengthP = matrixP[0].length; //得到多少部电影，决定list的列
        int axis=0;

        if(matrixQ[0].length!=matrixP.length){
            System.out.println("不符合矩阵的UV分解,两个矩阵无法相乘");
        }else {
            axis= matrixQ[0].length;
        }
        Double[][] list = new Double[lengthQ][lengthP]; //得分矩阵

        //R = Q * P
        for (int i = 0; i < lengthQ; i++) {
            for (int j = 0; j < lengthP; j++) {
                list[i][j]=0.0;
                for (int a = 0; a < axis; a++) {
                    list[i][j] += matrixQ[i][a]*matrixP[a][j];
                }
            }
        }

        return list;
    }

    /**
     * 得到用户潜在因子矩阵Q
     * *========================================*
     * *        因子1   因子.. 因子21           *
     * * 用户1                                  *
     * * size                                   *
     * *========================================*
     * @param q
     * @return Long[][]
     */
    public Double[][] getMatrixQ(List<UserLike> q){
        int size = q.size();
        Long[] userId = new Long[size];
        Double[][] matrixQ = new Double[size][21];
        int temp=0;
        Float n;//转型容器
        for (UserLike q1 : q) {
            userId[temp]=q1.getUserId();
            for (int i = 1; i <= 21; i++) {
                matrixQ[temp][i-1]=(double)q1.getMethod(i);
            }
            temp+=1;
        }

        return matrixQ;
    }

    /**
     * 得到电影潜在因子矩阵P
     * *========================================*
     * *        电影1   电影.. 电影size         *
     * * 因子1                                  *
     * * 因子..                                 *
     * * 因子21                                 *
     * *========================================*
     * @param p
     * @return Long[][]
     */
    public Double[][] getMatrixP(List<P> p){
        int size = p.size();
        Long[] movieId = new Long[size];
        Double[][] matrixP =  new Double[21][size];
        int temp=0;
        for (P p1 : p) {
            movieId[temp]=p1.getMovieId();
            for (int i = 1; i <= 21; i++) {
                matrixP[i-1][temp]=p1.getMethod(i);
            }
            temp+=1;
        }
        return matrixP;
    }

    /**
     * 最小损失
     * @param realR 真实的矩阵
     * @param R     估算的矩阵
     * @return
     */
    public Double lossFunction(Double[][] realR,Double[][] R){
        Double minLoss=0.0;
        Double temp;
        if(realR.length==R.length&&realR[0].length==R[0].length){//保证R矩阵和估算R矩阵是一样的规格
        int row=0;
            for (Double[] x : realR) {
                int rol=0;
                for (Double y : x) {
                    if (y!=null||y!=0.0){
                        temp= Math.pow((y-R[row][rol]),2);
                        if (minLoss==0.0||temp<minLoss){ //temp小于minLoss用于返回损失函数中非null的最小损失
                            minLoss=temp;
                        }
                    }
                    rol+=1;
                }
                row+=1;
            }
        }else {
            System.out.println("矩阵行列不对应无法进行计算");
        }
        return minLoss;
    }

    /**
     * 对于上述的平方损失函数，可以通过梯度下降法求解，梯度下降法的核心步骤，迭代处理
     * @param minLoss 最小损失
     * @param realR   真实的矩阵
     * @param R       估算的矩阵
     * @return
     */
    public double getLossFunction1(Double minLoss,Double[][] realR,Double[][] R){

        return 0.0;
    }


    /**
     * 得到横轴id列表
     * @param lists
     * @return
     */
    public Long[] getAxisP(List<P> lists){
        Long[] movieId = new Long[lists.size()];
        int temp=0;
        for (P p1 : lists) {
            movieId[temp]=p1.getMovieId();
            temp+=1;
        }
        return movieId;
    }

    /**
     * 得到纵轴id列表
     * @param lists
     * @return
     */
    public Long[] getAxisQ(List<UserLike> lists){
        Long[] userId = new Long[lists.size()];
        int temp=0;
        for (UserLike list : lists) {
            userId[temp]=list.getUserId();
            temp+=1;
        }
        return userId;
    }

    /**
     * 方便打印double二维数组
     * @param matrix
     */
    public void toPrint(Double[][] matrix){
        for (Double[] x : matrix) {
            for (Double y : x) {
                System.out.print(y+"\t");
            }
            System.out.println();
        }
    }
}
